package cn.bytepulse.filter;

import cn.bytepulse.pojo.AjaxResult;
import cn.bytepulse.pojo.CachedBodyHttpServletRequest;
import cn.bytepulse.pojo.CachedResponseWrapper;
import cn.bytepulse.runner.AnonymousAndCommercialScan;
import cn.bytepulse.utils.JsonUtils;
import cn.bytepulse.web.api.entity.ApiCustomer;
import cn.bytepulse.web.api.entity.ApiPems;
import cn.bytepulse.web.api.mapper.ApiCustomerMapper;
import cn.bytepulse.web.api.mapper.ApiPemsMapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.crypto.spec.IvParameterSpec;
import java.io.IOException;
import java.time.Instant;
import java.util.Optional;

import static cn.bytepulse.utils.CryptoUtils.*;
import static cn.bytepulse.utils.DateUtils.now;
import static cn.bytepulse.utils.ReqUtils.getIP;
import static cn.bytepulse.utils.ReqUtils.isPathMatching;

/**
 * @author jiejiebiezheyang
 * @since 2025-04-08 12:00
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class CommercialFilter extends OncePerRequestFilter {

    private final ApiPemsMapper apiPemsMapper;

    private final ApiCustomerMapper apiCustomerMapper;

    @Override
    protected void doFilterInternal(HttpServletRequest onceRequest, HttpServletResponse onceResponse, FilterChain filterChain) throws IOException, ServletException {
        onceResponse.setContentType("application/json");
        onceResponse.setCharacterEncoding("utf-8");
        // 包装原始请求，使其支持多次读取请求体
        CachedBodyHttpServletRequest request = new CachedBodyHttpServletRequest(onceRequest);
        CachedResponseWrapper response = new CachedResponseWrapper(onceResponse);
        // 判断路径
        String requestURI = request.getRequestURI();
        // 不是商用接口, 放行
        if (!isPathMatching(AnonymousAndCommercialScan.COMMERCIAL, requestURI)) {
            filterChain.doFilter(request, response);
            // 返回原始数据
            onceResponse.getOutputStream().write(response.getCachedData());
            return;
        }
        // 校验请求头
        // 应用id
        String appid = Optional.ofNullable(request.getHeader("X-Cyan-Appid")).orElse("");
        // 密钥(RSA加密)
        String cipher = Optional.ofNullable(request.getHeader("X-Cyan-cipher")).orElse("");
        // IV(RSA加密)
        String iv = Optional.ofNullable(request.getHeader("X-Cyan-Iv")).orElse("");
        // 随机数
        String nonce = Optional.ofNullable(request.getHeader("X-Cyan-Nonce")).orElse("");
        // 时间戳
        String timestamp = Optional.ofNullable(request.getHeader("X-Cyan-Timestamp")).orElse("");

        // 请求头指定格式签名
        String authorization = Optional.ofNullable(request.getHeader("Authorization")).orElse("");

        // 获取签名
        String sign = "X-Cyan-Appid:" + appid + "\n"
                + "X-Cyan-cipher:" + cipher + "\n"
                + "X-Cyan-Iv:" + iv + "\n"
                + "X-Cyan-Nonce:" + nonce + "\n"
                + "X-Cyan-Timestamp:" + timestamp;

        String sha256 = "";
        try {
            sha256 = getSHA256(sign);
        } catch (Exception e) {
            log.error("获取签名失败", e);
        }
        // 校验
        if ("".equals(sha256) || !sha256.equals(authorization)) {
            onceResponse.getWriter().println(JsonUtils.toJsonStr(AjaxResult.error(4001, "请求头校验失败")));
            return;
        }
        // 时间戳校验
        // 获取当前时间的时间戳（单位：秒）
        long currentTimestamp = Instant.now().getEpochSecond();
        // 计算时间差，单位：秒
        long timeDifference = currentTimestamp - Long.valueOf(timestamp);
        // 判断是否大于当前时间 或者 时间差是否超过 5 分钟（300 秒）
        if (Long.parseLong(timestamp) > currentTimestamp || timeDifference > 300) {
            onceResponse.getWriter().println(JsonUtils.toJsonStr(AjaxResult.error(4002, "请求超时")));
            return;
        }
        // appid 校验
        QueryWrapper<ApiCustomer> apiCustomerQueryWrapper = new QueryWrapper<>();
        apiCustomerQueryWrapper.eq("appid", appid);
        ApiCustomer apiCustomer = apiCustomerMapper.selectOne(apiCustomerQueryWrapper);
        if (appid.isBlank() || apiCustomer == null) {
            onceResponse.getWriter().println(JsonUtils.toJsonStr(AjaxResult.error(4003, "appid异常")));
            return;
        }
        // 检查是否启用
        String enable = apiCustomer.getEnable();
        if (!"1".equals(enable)) {
            onceResponse.getWriter().println(JsonUtils.toJsonStr(AjaxResult.error(4004, "应用已关闭")));
            return;
        }
        // 解析密钥
        QueryWrapper<ApiPems> apiPemsQueryWrapper = new QueryWrapper<>();
        apiPemsQueryWrapper.orderByDesc("create_time").last("limit 1");
        ApiPems apiPems = apiPemsMapper.selectOne(apiPemsQueryWrapper);
        // 获取RSA私钥
        String privatePem = apiPems.getPrivatePem();
        String cipher2 = "";
        String iv2 = "";
        try {
            cipher2 = decryptWithRSA(cipher, privatePem);
            iv2 = decryptWithRSA(iv, privatePem);
        } catch (Exception e) {
            log.error("密钥解析失败", e);
        }
        // 解密失败
        if (cipher2.isBlank() || iv2.isBlank()) {
            onceResponse.getWriter().println(JsonUtils.toJsonStr(AjaxResult.error(4005, "证书异常")));
            return;
        }
        // 获取加密的数据(base64)
        String requestBody = request.getCachedBodyAsString();
        // 解析数据
        IvParameterSpec ivParameterSpec = base64ToIV(iv2);
        String newBody = "";
        try {
            newBody = decryptWithAES(requestBody, cipher2, ivParameterSpec);
        } catch (Exception e) {
            log.error("数据解析失败", e);
        }
        request.setCachedBody(newBody);
        log.info("商用接口调用\nURL:\t\t{}\n请求方式:\t{}\n查询参数:\t{}\n请求参数:\t{}\n请求时间:\t{}\n请求IP:\t\t{}",
                requestURI,
                request.getMethod(),
                Optional.ofNullable(request.getQueryString()).orElse(""),
                "",
                now(),
                getIP());
        filterChain.doFilter(request, response);
        // 加密响应数据
        String responseBody = response.getCachedBody();
        String newResponseBody = "";
        try {
            newResponseBody = encryptWithAES(responseBody, cipher2, ivParameterSpec);
        } catch (Exception e) {
            log.error("响应数据加密失败", e);
            return;
        }
        byte[] modifiedData = newResponseBody.getBytes(onceResponse.getCharacterEncoding());
        onceResponse.setContentLength(modifiedData.length);
        onceResponse.getOutputStream().write(modifiedData);
        onceResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);
    }
}
